#include "rfid_api.h"
#include "rfid_board.h"
#include "fm1208_api.h"

#if _RFID_FUNCTION_ENABLE_

static CPU_CARD_STR CPU_CARD = {0};

void CPU_BaudRate_Set(uint8_t Mode)
{
  RfidPcdClearBitMask(JREG_TXMODE, 0x70);
  RfidPcdClearBitMask(JREG_RXMODE, 0x70);

  switch (Mode)
  {
  case 1: // 212kbps
  {
    RfidPcdSetBitMask(JREG_TXMODE, 0x10);
    RfidPcdSetBitMask(JREG_RXMODE, 0x10);
    RfidPcdWriteReg(JREG_MODWIDTH, 0x13);
  }
  break;

  case 2: // 424kbps
  {
    RfidPcdSetBitMask(JREG_TXMODE, 0x20);
    RfidPcdSetBitMask(JREG_RXMODE, 0x20);
    RfidPcdWriteReg(JREG_MODWIDTH, 0x09);
  }
  break;

  case 3: // 848kbps
  {
    RfidPcdSetBitMask(JREG_TXMODE, 0x30);
    RfidPcdSetBitMask(JREG_RXMODE, 0x30);
    RfidPcdWriteReg(JREG_MODWIDTH, 0x04);
  }
  break;

  default: // 106kbps
  {
    RfidPcdSetBitMask(JREG_TXMODE, 0x00);
    RfidPcdSetBitMask(JREG_RXMODE, 0x00);
    RfidPcdWriteReg(JREG_MODWIDTH, 0x26);
  }
  break;
  }
}

uint8_t CPU_FSD(uint8_t fsdi)
{
  uint8_t fsd;

  switch (fsdi & 0x0F)
  {
  case 0:
    fsd = 14;
    break;
  case 1:
    fsd = 22;
    break;
  case 2:
    fsd = 30;
    break;
  case 3:
    fsd = 38;
    break;
  case 4:
    fsd = 46;
    break;
  case 5:
    fsd = 62;
    break;
  case 6:
    fsd = 94;
    break;
  case 7:
    fsd = 126;
    break;
  case 8:
    fsd = 254;
    break;
  default:
    fsd = 255;
    break;
  }

  return fsd;
}

uint16_t CPU_FWT(uint8_t fwi)
{
  unsigned int fwt;

  switch (fwi & 0x0F)
  {
  case 0:
    fwt = 1; // 协议定义为302us
    break;
  case 1:
    fwt = 1; // 604us
    break;
  case 2:
    fwt = 1; // 1208us
    break;
  case 3:
    fwt = 3; // 2416us
    break;
  case 4:
    fwt = 5; // 4833us
    break;
  case 5:
    fwt = 10; // 9666us
    break;
  case 6:
    fwt = 20; // 19332us
    break;
  case 7:
    fwt = 40; // 38664us
    break;
  case 8:
    fwt = 80; // 77328us
    break;
  case 9:
    fwt = 155; // 154657us
    break;
  case 10:
    fwt = 310; // 309314us
    break;
  case 11:
    fwt = 619; // 618628us
    break;
  case 12:
    fwt = 1238; // 1237257us
    break;
  case 13:
    fwt = 2475; // 2474515us
    break;
  case 14:
    fwt = 4950; // 4949031us
    break;
  default:
    fwt = 5;
    break;
  }

  return fwt;
}

bool CPU_NAK(uint8_t *pRxDataBuff, uint8_t *pRxDataSize)
{
  uint16_t RxDataSize = 0;
  uint8_t DataSize = 0, OperationDataBuff[MAXRLEN] = {0};

  if ((pRxDataBuff != NULL) && (pRxDataSize != NULL))
  {
    RfidPcdSetTimer(CPU_CARD.FWT);

    OperationDataBuff[DataSize++] = (CPU_CARD.PCB & 0x0F) | 0xB0;
    if (CPU_CARD.PCB & 0x08)
    {
      OperationDataBuff[DataSize++] = CPU_CARD.CID;
    }

    uint8_t NakResult = RfidPcdCommunication(PCD_TRANSCEIVE, OperationDataBuff, DataSize, pRxDataBuff, &RxDataSize);
    if (NakResult == MI_OK)
    {
      CPU_CARD.PCB = pRxDataBuff[0];
      if (CPU_CARD.PCB & 0xF0)
      {
        CPU_CARD.WTXM = (CPU_CARD.PCB & 0x08) ? pRxDataBuff[2] : pRxDataBuff[1];
      }

      *pRxDataSize = (uint8_t)(RxDataSize / 8);
      return true;
    }
  }

  return false;
}

bool CPU_ACK(uint8_t *pRxDataBuff, uint8_t *pRxDataSize)
{
  uint16_t RxDataSize = 0;
  uint8_t DataSize = 0, OperationDataBuff[MAXRLEN] = {0};

  if ((pRxDataBuff != NULL) && (pRxDataSize != NULL))
  {
    RfidPcdSetTimer(CPU_CARD.FWT);

    OperationDataBuff[DataSize++] = (CPU_CARD.PCB & 0x0F) | 0xA0;
    if (CPU_CARD.PCB & 0x08)
    {
      OperationDataBuff[DataSize++] = CPU_CARD.CID;
    }

    uint8_t AakResult = RfidPcdCommunication(PCD_TRANSCEIVE, OperationDataBuff, DataSize, pRxDataBuff, &RxDataSize);
    if (AakResult == MI_OK)
    {
      CPU_CARD.PCB = pRxDataBuff[0];
      if ((CPU_CARD.PCB & 0xF0) == 0xF0)
      {
        CPU_CARD.WTXM = (CPU_CARD.PCB & 0x08) ? pRxDataBuff[2] : pRxDataBuff[1];
      }

      *pRxDataSize = (uint8_t)(RxDataSize / 8);
      return true;
    }
  }

  return false;
}

bool Rats(uint8_t Param1, uint8_t Param2, uint8_t *pRxDataBuff, uint8_t *pRxDataSize)
{
  uint16_t RxDataLength = 0;
  uint8_t RatsResult = MI_ERR, Offset = 0;
  uint8_t OperationDataBuff[MAXRLEN] = {0};

  if ((pRxDataBuff != NULL) && (pRxDataSize != NULL))
  {
    CPU_CARD.FWT = 5;
    RfidPcdSetTimer(CPU_CARD.FWT);

    OperationDataBuff[0] = 0xE0;
    OperationDataBuff[1] = Param2;

    CPU_CARD.CID = Param2 & 0x0F;
    CPU_CARD.PCB = Param1;

    RatsResult = RfidPcdCommunication(PCD_TRANSCEIVE, OperationDataBuff, 2, pRxDataBuff, &RxDataLength);

    *pRxDataSize = (uint8_t)(RxDataLength / 8);
    if ((RatsResult == MI_OK) && (*pRxDataSize == pRxDataBuff[0]))
    {
      Offset = 0;

      CPU_CARD.ATS.TL = *pRxDataBuff;
      CPU_CARD.ATS.T0 = *(pRxDataBuff + 1);
      CPU_CARD.ATS.FSCI = CPU_CARD.ATS.T0 & 0x0F;
      CPU_CARD.FSD = CPU_FSD(CPU_CARD.ATS.FSCI);

      if (CPU_CARD.ATS.T0 & 0x40)
      {
        CPU_CARD.ATS.TA1 = *(pRxDataBuff + 2 + Offset);
        Offset++;
      }
      else
      {
        CPU_CARD.ATS.TA1 = 0;
      }

      if (CPU_CARD.ATS.T0 & 0x20)
      {
        CPU_CARD.ATS.TB1 = *(pRxDataBuff + 2 + Offset);
        CPU_CARD.ATS.FWI = (CPU_CARD.ATS.TB1 & 0xF0) >> 4;
        CPU_CARD.FWT = CPU_FWT(CPU_CARD.ATS.FWI);
        CPU_CARD.ATS.SFGI = CPU_CARD.ATS.TB1 & 0x0F;

        Offset++;
      }
      else
      {
        CPU_CARD.ATS.TB1 = 0;
      }

      if (CPU_CARD.ATS.T0 & 0x10)
      {
        CPU_CARD.ATS.TC1 = *(pRxDataBuff + 2 + Offset);
        Offset++;
      }
      else
      {
        CPU_CARD.ATS.TC1 = 0;
      }

      return true;
    }
  }

  return false;
}

bool CPU_PPS(uint8_t pps0, uint8_t pps1, uint8_t *pRxDataBuff, uint8_t *pRxDataSize)
{
  uint16_t RxDataLength = 0;
  uint8_t DataSize = 0, OperationDataBuff[MAXRLEN] = {0};

  if ((pRxDataBuff != NULL) && (pRxDataSize != NULL))
  {
    RfidPcdSetTimer(CPU_CARD.FWT);

    OperationDataBuff[DataSize++] = 0xD0 | CPU_CARD.CID;
    OperationDataBuff[DataSize++] = pps0;

    if ((pps0 & 0x10) == 0x10)
    {
      OperationDataBuff[DataSize++] = pps1;
    }

    uint8_t PpsResult = RfidPcdCommunication(PCD_TRANSCEIVE, OperationDataBuff, DataSize, pRxDataBuff, &RxDataLength);
    if (PpsResult == MI_OK)
    {
      *pRxDataSize = (uint8_t)(RxDataLength / 8);
      return true;
    }
  }

  return false;
}

void CPU_PCB_CONVER(void)
{
  CPU_CARD.PCB = CPU_CARD.PCB ^ 0x01;
}

bool CPU_TPDU(uint8_t *pTxDataBuff, uint8_t TxDataSize, uint8_t *pRxDataBuff, uint8_t *pRxDataSize)
{
  uint16_t RxDataLength = 0;

  if ((pTxDataBuff != NULL) && (pRxDataBuff != NULL) && (pRxDataSize != NULL))
  {
    RfidPcdSetTimer(CPU_CARD.FWT);
    uint8_t PcbConverResult = RfidPcdCommunication(PCD_TRANSCEIVE, pTxDataBuff, TxDataSize, pRxDataBuff, &RxDataLength);
    if (PcbConverResult == MI_OK)
    {
      *pRxDataSize = (uint8_t)(RxDataLength / 8);
      return true;
    }
  }

  return false;
}

bool CPU_I_Block(uint8_t *pTxDataBuff, uint8_t TxDataSize, uint8_t *pRxDataBuff, uint8_t *pRxDataSize)
{
  uint16_t RxDataLength = 0;
  uint8_t DataSize = 0, OperationDataBuff[128] = {0};

  if ((pTxDataBuff != NULL) && (pRxDataBuff != NULL) && (pRxDataSize != NULL))
  {
  CPU_I_BLOCK_START:
    RfidPcdSetTimer(CPU_CARD.FWT);

    if (CPU_CARD.PCB & 0x08)
    {
      OperationDataBuff[0] = CPU_CARD.PCB;
      OperationDataBuff[1] = CPU_CARD.CID;
      memmove(OperationDataBuff + 2, pTxDataBuff, TxDataSize);
      DataSize = TxDataSize + 2;
    }
    else
    {
      OperationDataBuff[0] = CPU_CARD.PCB;
      memmove(OperationDataBuff + 1, pTxDataBuff, TxDataSize);
      DataSize = TxDataSize + 1;
    }

    uint8_t OperationResult = RfidPcdCommunication(PCD_TRANSCEIVE, OperationDataBuff, DataSize, OperationDataBuff, &RxDataLength);
    if (OperationResult == MI_OK)
    {
      CPU_CARD.PCB = OperationDataBuff[0];
      if ((CPU_CARD.PCB & 0xF0) == 0xF0)
      {
        CPU_CARD.WTXM = (CPU_CARD.PCB & 0x08) ? *(OperationDataBuff + 2) : *(OperationDataBuff + 1);
      }

      if ((RxDataLength / 8) <= MAXRLEN)
      {
        *pRxDataSize = (uint8_t)(RxDataLength / 8);
        memcpy(pRxDataBuff, OperationDataBuff, RxDataLength / 8);
      }

      return true;
    }
    else
    {
      bool NakResult = false;

      for (uint8_t i = 0; i < 3; i++)
      {
        NakResult = CPU_NAK(OperationDataBuff, pRxDataSize);
        if (NakResult == true)
        {
          CPU_CARD.PCB = *OperationDataBuff;
          if ((CPU_CARD.PCB & 0xF0) == 0xA0)
          {
            CPU_CARD.PCB = CPU_CARD.PCB & 0x0F;
            CPU_PCB_CONVER();

            goto CPU_I_BLOCK_START;
          }

          if ((CPU_CARD.PCB & 0xF0) == 0xF0)
          {
            CPU_CARD.WTXM = (CPU_CARD.PCB & 0x08) ? *(OperationDataBuff + 2) : *(OperationDataBuff + 1);
          }

          return true;
        }
      }
    }
  }

  return false;
}

bool CPU_R_Block(uint8_t *pRxDataBuff, uint8_t *pRxDataSize)
{
  uint16_t RxDataLength = 0;
  uint8_t DataSize = 0, OperationDataBuff[512] = {0};

  if ((pRxDataBuff != NULL) && (pRxDataSize != NULL))
  {
  CPU_R_BLOCK_START:
    RfidPcdSetTimer(CPU_CARD.FWT);

    OperationDataBuff[DataSize++] = (CPU_CARD.PCB & 0x0F) | 0xA0;
    if (CPU_CARD.PCB & 0x08)
    {
      OperationDataBuff[DataSize++] = CPU_CARD.CID;
    }

    uint8_t OperationResult = RfidPcdCommunication(PCD_TRANSCEIVE, OperationDataBuff, DataSize, OperationDataBuff, &RxDataLength);
    if (OperationResult == MI_OK)
    {
      CPU_CARD.PCB = OperationDataBuff[0];
      if ((CPU_CARD.PCB & 0xF0) == 0xA0)
      {
        CPU_CARD.PCB = CPU_CARD.PCB & 0x0F;

        CPU_PCB_CONVER();
        goto CPU_R_BLOCK_START;
      }

      if ((CPU_CARD.PCB & 0xF0) == 0xF0)
      {
        CPU_CARD.WTXM = (CPU_CARD.PCB & 0x08) ? *(OperationDataBuff + 2) : *(OperationDataBuff + 1);
      }

      CPU_PCB_CONVER();

      if ((RxDataLength / 8) <= MAXRLEN)
      {
        memcpy(pRxDataBuff, OperationDataBuff, RxDataLength / 8);
        *pRxDataSize = (uint8_t)(RxDataLength / 8);

        return true;
      }

      return false;
    }
    else
    {
      bool NakResult = false;

      for (uint8_t i = 0; i < 3; i++)
      {
        NakResult = CPU_NAK(OperationDataBuff, pRxDataSize);
        if (NakResult == true)
        {
          CPU_CARD.PCB = *OperationDataBuff;
          if ((CPU_CARD.PCB & 0xF0) == 0xF0)
          {
            CPU_CARD.WTXM = (CPU_CARD.PCB & 0x08) ? *(OperationDataBuff + 2) : *(OperationDataBuff + 1);
          }

          CPU_PCB_CONVER();

          if (RxDataLength / 8 <= MAXRLEN)
          {
            memcpy(pRxDataBuff, OperationDataBuff, RxDataLength / 8);
            *pRxDataSize = (uint8_t)(RxDataLength / 8);

            return true;
          }

          return false;
        }
      }
    }
  }

  return false;
}

bool CPU_WTX(uint8_t wtxm, uint8_t *pRxDataBuff, uint8_t *pRxDataSize)
{
  uint16_t RxDataLength = 0;
  uint8_t DataSize = 0, OperationDataBuff[MAXRLEN] = {0};

  if ((pRxDataBuff != NULL) && (pRxDataSize != NULL))
  {
    RfidPcdSetTimer(CPU_CARD.FWT);

    OperationDataBuff[DataSize++] = CPU_CARD.PCB;
    if (CPU_CARD.PCB & 0x08)
    {
      OperationDataBuff[DataSize++] = CPU_CARD.CID;
      OperationDataBuff[DataSize++] = wtxm;
    }
    else
    {
      OperationDataBuff[DataSize++] = wtxm;
    }

    uint8_t OperationResult = RfidPcdCommunication(PCD_TRANSCEIVE, OperationDataBuff, DataSize, pRxDataBuff, &RxDataLength);
    if (OperationResult == MI_OK)
    {
      *pRxDataSize = (uint8_t)(RxDataLength / 8);
      return true;
    }
  }

  return false;
}

bool CPU_Deselect(void)
{
  uint16_t RxDataLength = 0;
  uint8_t DataSize = 0, OperationDataBuff[MAXRLEN] = {0};

  RfidPcdSetTimer(CPU_CARD.FWT);
  CPU_CARD.PCB = 0xC2 | (CPU_CARD.PCB & 0x08);
  OperationDataBuff[DataSize++] = CPU_CARD.PCB;
  if (CPU_CARD.PCB & 0x08)
  {
    OperationDataBuff[DataSize++] = CPU_CARD.CID;
  }

  uint8_t OperationResult = RfidPcdCommunication(PCD_TRANSCEIVE, OperationDataBuff, DataSize, OperationDataBuff, &RxDataLength);
  if ((OperationResult == MI_OK) && (CPU_CARD.PCB == OperationDataBuff[0]))
    return true;

  return false;
}

bool APDU_Exchange(uint8_t *pDataIn, uint8_t SizeIn, uint8_t *pDataOut, uint16_t *pSizeOut)
{
  bool Result = false;
  uint8_t TpduRxLenght = 0;

  if ((pDataIn != NULL) && (pDataOut != NULL) && (pSizeOut != NULL))
  {
    *pSizeOut = 0;

    if ((SizeIn == 0) && (SizeIn > CPU_CARD.FSD))
      return false;

    Result = CPU_I_Block(pDataIn, SizeIn, pDataOut, (uint8_t *)pSizeOut);
    if (Result == true)
    {
      while (1)
      {
        if ((CPU_CARD.PCB & 0xF0) == 0xF0)
        {
          Result = CPU_WTX(CPU_CARD.WTXM, pDataOut, &TpduRxLenght);
          if (Result != true)
            return false;

          if ((CPU_CARD.PCB & 0xF0) == 0xF0)
          {
            CPU_CARD.WTXM = (CPU_CARD.PCB & 0x08) ? *(pDataOut + 2) : *(pDataOut + 1);
          }

          *pSizeOut = TpduRxLenght;
        }

        if ((CPU_CARD.PCB & 0xF0) == 0x10)
        {
          CPU_PCB_CONVER();
          Result = CPU_R_Block(pDataOut + (*pSizeOut), &TpduRxLenght);
          if (Result != true)
            return false;

          CPU_CARD.PCB = *(pDataOut + (*pSizeOut));
          if (CPU_CARD.PCB & 0x08)
          {
            for (uint8_t i = 0; i < TpduRxLenght - 2; i++)
            {
              *(pDataOut + i + (*pSizeOut)) = *(pDataOut + i + (*pSizeOut) + 2);
            }

            *pSizeOut = *pSizeOut + TpduRxLenght - 2;
          }
          else
          {
            for (uint8_t i = 0; i < TpduRxLenght - 1; i++)
            {
              *(pDataOut + i + (*pSizeOut)) = *(pDataOut + i + (*pSizeOut) + 1);
            }

            *pSizeOut = *pSizeOut + TpduRxLenght - 1;
          }
        }

        if ((CPU_CARD.PCB & 0xF0) == 0x00)
        {
          if (Result == true)
          {
            CPU_PCB_CONVER();
            if (CPU_CARD.PCB & 0x08)
            {
              *pSizeOut = *pSizeOut - 2;
              memmove(pDataOut, pDataOut + 2, *pSizeOut);
            }
            else
            {
              *pSizeOut = *pSizeOut - 1;
              memmove(pDataOut, pDataOut + 1, *pSizeOut);
            }

            return true;
          }
        }
      }
    }
  }

  return false;
}

#endif
